package org.xebia.roo.addon.compareto;

import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.List;

import org.springframework.roo.classpath.PhysicalTypeIdentifierNamingUtils;
import org.springframework.roo.classpath.PhysicalTypeMetadata;
import org.springframework.roo.classpath.details.FieldMetadata;
import org.springframework.roo.classpath.details.MethodMetadata;
import org.springframework.roo.classpath.details.MethodMetadataBuilder;
import org.springframework.roo.classpath.details.annotations.AnnotatedJavaType;
import org.springframework.roo.classpath.itd.AbstractItdTypeDetailsProvidingMetadataItem;
import org.springframework.roo.classpath.itd.InvocableMemberBodyBuilder;
import org.springframework.roo.metadata.MetadataIdentificationUtils;
import org.springframework.roo.model.ImportRegistrationResolver;
import org.springframework.roo.model.JavaSymbolName;
import org.springframework.roo.model.JavaType;
import org.springframework.roo.project.LogicalPath;
import org.springframework.roo.support.style.ToStringCreator;
import org.springframework.roo.support.util.Assert;
import org.springframework.roo.support.util.CollectionUtils;

public class ComparetoMetadata extends AbstractItdTypeDetailsProvidingMetadataItem {

    private static final String PROVIDES_TYPE_STRING = ComparetoMetadata.class.getName();
    private static final String PROVIDES_TYPE = MetadataIdentificationUtils.create(PROVIDES_TYPE_STRING);

    public static final String getMetadataIdentiferType() {
        return PROVIDES_TYPE;
    }

    public static final String createIdentifier(JavaType javaType, LogicalPath path) {
        return PhysicalTypeIdentifierNamingUtils.createIdentifier(PROVIDES_TYPE_STRING, javaType, path);
    }

    public static final JavaType getJavaType(String metadataIdentificationString) {
        return PhysicalTypeIdentifierNamingUtils.getJavaType(PROVIDES_TYPE_STRING, metadataIdentificationString);
    }

    public static final LogicalPath getPath(String metadataIdentificationString) {
        return PhysicalTypeIdentifierNamingUtils.getPath(PROVIDES_TYPE_STRING, metadataIdentificationString);
    }

    public static boolean isValid(String metadataIdentificationString) {
        return PhysicalTypeIdentifierNamingUtils.isValid(PROVIDES_TYPE_STRING, metadataIdentificationString);
    }

    private List<FieldMetadata> compareToFields;

    public ComparetoMetadata(String identifier, JavaType aspectName, PhysicalTypeMetadata governorPhysicalTypeMetadata,
            List<FieldMetadata> compareToFields) {
        super(identifier, aspectName, governorPhysicalTypeMetadata);
        Assert.isTrue(isValid(identifier), "Metadata identification string '" + identifier + "' does not appear to be a valid");

        this.compareToFields = compareToFields;
        if (!CollectionUtils.isEmpty(compareToFields)) {
            JavaType comparableInterface = new JavaType("java.lang.Comparable");
            builder.addImplementsType(comparableInterface);
            builder.addMethod(getCompareToMethod());
        }
        itdTypeDetails = builder.build();

    }

    private MethodMetadata getCompareToMethod() {
        final JavaType parameterType = JavaType.OBJECT;
        String parameterName = "obj";
        
        final List<JavaSymbolName> parameterNames = Arrays.asList(new JavaSymbolName(parameterName));
        
        final InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder();
        
        final ImportRegistrationResolver imports = builder.getImportRegistrationResolver();
        imports.addImport(new JavaType("org.apache.commons.lang3.builder.CompareToBuilder"));
        
        final String typeName = destination.getSimpleTypeName();
        bodyBuilder.appendFormalLine("if (!(" + parameterName + " instanceof " + typeName + ")) {");
        bodyBuilder.indent();
        bodyBuilder.appendFormalLine("return -1;");
        bodyBuilder.indentRemove();
        bodyBuilder.appendFormalLine("}");

        bodyBuilder.appendFormalLine(typeName + " rhs = (" + typeName + ") " + parameterName + ";");
        final StringBuilder builder = new StringBuilder();
        builder.append("return new CompareToBuilder()");

        for (final FieldMetadata field : compareToFields) {
            builder.append(".append(" + field.getFieldName() + ", rhs." + field.getFieldName() + ")");
        }
        builder.append(".toComparison();");

        bodyBuilder.appendFormalLine(builder.toString());
        
        final MethodMetadataBuilder methodBuilder = new MethodMetadataBuilder(getId(), Modifier.PUBLIC, new JavaSymbolName("compareTo"),
                JavaType.INT_PRIMITIVE, AnnotatedJavaType.convertFromJavaTypes(parameterType), parameterNames, bodyBuilder);
        return methodBuilder.build();
    }

    public String toString() {
        ToStringCreator tsc = new ToStringCreator(this);
        tsc.append("identifier", getId());
        tsc.append("valid", valid);
        tsc.append("aspectName", aspectName);
        tsc.append("destinationType", destination);
        tsc.append("governor", governorPhysicalTypeMetadata.getId());
        tsc.append("itdTypeDetails", itdTypeDetails);
        return tsc.toString();
    }
}
